package com.motu.monstercity.server.gate;

import com.motu.monstercity.module.common.network.DataDecoded;
import com.motu.monstercity.module.common.network.DataEncoded;
import com.motu.monstercity.module.common.network.ResponseUtils;
import com.motu.monstercity.module.common.redis.RedisKey;
import com.motu.monstercity.module.common.utility.CmdConstant;
import com.motu.monstercity.module.common.utility.ServerParam;
import com.motu.monstercity.protocol.CmdEnum;
import com.motu.monstercity.protocol.CmdEnum.EnumCmdID;
import com.motu.monstercity.protocol.CsGameSystem.CSGameSystemLogicErrorResponse;
import com.motu.monstercity.protocol.CsGameSystem.CSGameSystemLogicErrorResponseType;
import com.motu.monstercity.protocol.CsGameSystem.CSGameSystemReloadNoticeResponse;
import com.motu.monstercity.module.common.utility.EventBusConstant;
import com.motu.vertx.module.utility.base.ServerConfig;
import com.motu.vertx.module.utility.commondata.ProtocolMonitor;
import com.motu.vertx.module.utility.main.MainService;
import com.motu.vertx.module.utility.monitor.GateMonitorManager;
import com.motu.vertx.module.utility.monitor.MonitorManager;
import com.motu.vertx.module.utility.mysql.MysqlClient;
import com.motu.vertx.module.utility.mysql.MysqlTool;
import com.motu.vertx.module.utility.redis.RedisTools;
import com.motu.vertx.module.utility.toolset.ErrorWord;
import com.motu.vertx.module.utility.toolset.ErrorWordManager;
import com.motu.vertx.module.utility.toolset.TimeUtils;
import com.motu.vertx.module.utility.toolset.Tool;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;

public class SocketManager {
    public static final Logger logger = LoggerFactory.getLogger(SocketManager.class);
    private static ConcurrentHashMap<Long, ServerWebSocket> socketList = new ConcurrentHashMap<Long, ServerWebSocket>();
    private static ConcurrentHashMap<Integer, Integer> serverOnlineList = new ConcurrentHashMap<Integer, Integer>();
    //	private  ConcurrentHashMap<String,SessionInfo> sessionList = new ConcurrentHashMap<String,SessionInfo>();//未登录的链接
    public static ConcurrentHashMap<Long, ConcurrentLinkedQueue<DataDecoded>> userRequestQueueMap = new ConcurrentHashMap<>();// 用户消息队列

    // 消息队列
    public static ConcurrentHashMap<Integer, BlockingQueue<MessageInfo>> messageQueueMap = new ConcurrentHashMap<>();
    public static ConcurrentHashMap<Integer, ExecutorService> messageServiceMap = new ConcurrentHashMap<>();
    public static ConcurrentHashMap<Long, MessageCache> messageCacheMap = new ConcurrentHashMap<>();


    public static void init(Vertx vertx) {
        if (!ServerConfig.isLocalServer()) {
            vertx.setPeriodic(1000L, handler -> {
                //			checkSessionTimeout();
                updateServerOnline();

                // 定时打印消息队列数量
//                if (SocketManager.isMessageQueueWay()) {
//                    logger.info("ip = " + Tool.getHostIp() + " total message queue num = " + SocketManager.getTotalMessageQueueNum());
//                }
            });
        }

        // 初始化消息队列线程
//        for (int i = 0; i < ServerParam.GATE_MESSAGE_POOL_SIZE; i++) {
//            BlockingQueue<MessageInfo> messageQueue = new LinkedBlockingQueue<>(ServerParam.GATE_MESSAGE_QUEUE_MAX);
//            messageQueueMap.put(i, messageQueue);
//            ExecutorService messageService = Executors.newSingleThreadExecutor();
//            messageServiceMap.put(i, messageService);
//            MessageThread thread = new MessageThread(messageQueue);
//            messageService.execute(thread);
//        }

        // 清空在线玩家数量
        RedisTools.del(RedisKey.getUserOnlineKey());

    }

    /***
     * 根据socket获取对应的userid
     * @param socket
     * @return
     */
//	public long getUserIdBySocket(ServerWebSocket socket){
////		logger.info("getUserIdBySocket socket host:"+socket.remoteAddress().host()+" port:"+socket.remoteAddress().port());
////		for(Long key:socketList.keySet()){
////			ServerWebSocket s = socketList.get(key).getSocket();
////			//			logger.info("userId:"+key+" host:"+s.remoteAddress().host()+" port:"+s.remoteAddress().port());
////			if(s.remoteAddress().host().equalsIgnoreCase(socket.remoteAddress().host())&&s.remoteAddress().port() == (socket.remoteAddress().port())){
////				return key;
////			}
////		}
////		return 0l;
//		String uid = socket.headers().get("uid");
//		if (Tool.isEmpty(uid)){
//			return 0;
//		}
//		return Long.parseLong(uid);
//	}

    /***
     * 更新各个服务器的在线数到redis，如果跟上一秒的人数不同则更新
     */
    private static void updateServerOnline() {
        ConcurrentHashMap<Integer, Integer> tmp = new ConcurrentHashMap<Integer, Integer>();
        for (ServerWebSocket info : socketList.values()) {
            int serverId = SocketInfo.getServerId(info);
            int value = 1;
            if (tmp.containsKey(serverId)) {
                value += tmp.get(serverId);
            }
            tmp.put(serverId, value);
        }
        Enumeration<Integer> en = tmp.keys();
        while (en.hasMoreElements()) {
            int serverId = en.nextElement();
            int lastNum = serverOnlineList.get(serverId) == null ? 0 : serverOnlineList.get(serverId).intValue();
            int curNum = tmp.get(serverId);
            if (lastNum != curNum) {
                serverOnlineList.put(serverId, curNum);
                String key = RedisKey.getServerOnlineKey(serverId);
                String field = ServerConfig.getGateName();
                RedisTools.setHashField(key, field, "" + curNum);
            }
        }
    }
//	public ConcurrentHashMap<String,SessionInfo> getSessionList(){
//		return sessionList;
//	}

    /***
     * 添加一个session
     * @param ws
     */
//	public void addSession(ServerWebSocket socket){
//		String key = socket.remoteAddress().host()+":"+socket.remoteAddress().port();
//		//		logger.info("#### addSession:"+key);
//		sessionList.put(key, new SessionInfo(socket));
//	}

    /***
     * 删除一个session
     * @param key
     */
//	public void removeSession(String key){
//		logger.info("**** removeSession:"+key);
//		sessionList.remove(key);
//	}

    /***
     * 添加一个新的socket链接
     * @param account
     * @param uid
     * @param serverId
     * @param socket
     * @param language
     */
    public static void addSocket(String account, long uid, int serverId, ServerWebSocket socket, int language, JsonObject userGateObj, int platform) {
        logger.info("addSocket uid:" + uid + " host:" + socket.remoteAddress().host() + " port:" + socket.remoteAddress().port() + " userGateObj:" + userGateObj.toString());
        if (socketList.containsKey(uid)) {//顶号
            ServerWebSocket oldSocket = socketList.get(uid);

			// 有可能出现CMD_GAME_INIT协议重复发送的情况，这时候socket对象不变，第二次可以不处理
			if (socket.remoteAddress().host().equals(oldSocket.remoteAddress().host()) && socket.remoteAddress().port() == oldSocket.remoteAddress().port()) {
				return;
			}

            endRepeatdLogin(uid, oldSocket, SocketInfo.getLanguage(oldSocket));
        }

        setGateObject(uid, userGateObj);
        // 设置socket信息
        SocketInfo.setSocketInfo(socket, account, uid, serverId, language, platform);
        socketList.put(uid, socket);

        // 更新总的玩家在线数量
        if (ServerParam.USER_ONLINE_MAX > 0) {
            RedisTools.add(RedisKey.getUserOnlineKey(), 1, h -> {
            });
        }

//		MainVerticle.instance.socketManager.removeSession(socket.remoteAddress().host()+":"+socket.remoteAddress().port());

        //通知其他网关服检查是否有同个玩家的登录
        String address = EventBusConstant.getAddress(EventBusConstant.GATE_CHECKREPEATEDLOGIN);
        EventBus eventBus = MainService.getVertx().eventBus();
        JsonObject obj = new JsonObject().put("userid", uid).put("gatename", ServerConfig.getGateName()).put("language", language);
        eventBus.publish(address, obj);
    }

    /***
     * 保存玩家连接的gate名称，已经设备信息，deviceid等
     * @param userId
     * @param userGateObj
     */
    public static void setGateObject(long userId, JsonObject userGateObj) {
		// 保存网关名称（离线马上删除）
		String userGateKey = RedisKey.getUserGateKey(userId);
		RedisTools.set(userGateKey, userGateObj.getString("gate_name"));
		RedisTools.expire(userGateKey, Tool.DAY);//设置过期时间为24小时

		// 保存玩家最后一次登录设备信息（离线不删除）
		String userLoginDeviceKey = RedisKey.getUserLoginDeviceKey(userId);
		RedisTools.set(userLoginDeviceKey, userGateObj.toString());
		RedisTools.expire(userLoginDeviceKey, ServerParam.REDIS_EXPIRE_TIME);//设置过期时间
    }

    /**
     * 断开连接并且踢下线
     */
    public static void sockEndAndKick(ServerWebSocket socket, int language, ErrorWord errorWord) {
        CSGameSystemLogicErrorResponse.Builder response = CSGameSystemLogicErrorResponse.newBuilder();
        response.setRet(errorWord.value);
        response.setErrMsg(ErrorWordManager.get(EnumCmdID.CMD_DEFAULT.name(), errorWord, language));
        response.setType(CSGameSystemLogicErrorResponseType.kick);// 踢下线
        try {
            if (!socket.isClosed()) {
                socket.end(DataEncoded.encode(CmdEnum.EnumCmdID.CMD_GAME_SYSTEM_LOGIC_ERROR_VALUE, (byte) 0, response.build().toByteArray(), 0));
            }
        } catch (Exception e) {
            logger.error(Tool.getException(e));
        }
    }

    /***
     * 处理顶号登陆
     */
    public static void endRepeatdLogin(long uid, ServerWebSocket oldSocket, int language) {
        logger.info(" ### endRepeatdLogin socket ip = " + oldSocket.remoteAddress().host() + " port = " + oldSocket.remoteAddress().port() + " userId = " + uid);
        sockEndAndKick(oldSocket, language, ErrorWord.LOGIN_REPLACE);
        socketList.remove(uid);
        // 更新总的玩家在线数量
        if (ServerParam.USER_ONLINE_MAX > 0) {
            RedisTools.sub(RedisKey.getUserOnlineKey(), 1, h -> {
            });
        }
//		removeUserGateKey(uid);//这里不能删除redis中的网关信息，不然移除其他网关服的连接会导致redis的网关信息被删除
    }

    /***
     * 踢掉用户
     */
    public static void kickUser(long uid, ServerWebSocket socket, int language, ErrorWord er) {
        sockEndAndKick(socket, language, er);
        removeSocket(uid, socket);
    }

    /***
     * 踢掉用户
     */
    public static void kickUser(ServerWebSocket socket, ErrorWord er) {
        long userid = SocketInfo.getUserId(socket);
        int language = SocketInfo.getLanguage(socket);
        sockEndAndKick(socket, language, er);
        removeSocket(userid, socket);
    }

    /***
     * 踢掉用户
     */
    public static void kickUser(long userid, ErrorWord er) {
        ServerWebSocket socket = SocketManager.getSocket(userid);
        if (socket != null) {
            int language = SocketInfo.getLanguage(socket);
            sockEndAndKick(socket, language, er);
            removeSocket(userid, socket);
        }
    }

    /***
     * 获取SocketInfo列表
     * @return
     */
    public static ConcurrentHashMap<Long, ServerWebSocket> getSocketList() {
        return socketList;
    }

    /***
     * 获取一个socket
     * @param uid 用户id
     * @return
     */
    public static ServerWebSocket getSocket(long uid) {
        if (socketList.containsKey(uid)) {
            return socketList.get(uid);
        }
        return null;
    }

    /**
     * 下线时更新userdailyonline 用户的今日在线总时长
     * TODO userdailyonline表已经删除，可能会有类似需求，用于参考
     */
    public void updateUserdailyonline(long userId, int startTime) {
//		String key = "userdailyonline." + userId;
//		// !!! 新规范下redis字段名会可能和数据库不一样 !!!
//		RedisTools.getHashJsonObject(MainVerticle.instance.redisClient, key, handler->{
//			if (handler.succeeded()) {
//				JsonObject obj = handler.result();
//				if (obj.containsKey("updatetime1")) {
//					int onlinetime = 0;  //这次上线属于今天的在线时长
//					if (Tool.isOneDay(startTime, TimeUtils.getCurTime())) {//上线时间和下线时间都是今天
//						onlinetime = TimeUtils.getCurTime() - startTime;
//					} else {  //昨天上线,今天才下线
//						int stime = Tool.getTimeStrIntValue2(TimeUtils.getCurTime(), 0, 0 , 0);//今天的0点0分0秒
//						onlinetime = TimeUtils.getCurTime() - stime;
//					}
//
//					int todayonline = obj.containsKey("todayonline") ? Integer.parseInt(obj.getString("todayonline")) : 0;  //上次更新的在线总时长
//					int updatetime1 = Integer.parseInt(obj.getString("updatetime1"));  //更新时间
//					if (Tool.isToday(updatetime1)) {
//						todayonline += onlinetime;  //同一天累加在线时长
//					} else {
//						todayonline = onlinetime;
//					}
//
//					obj.put("todayonline", todayonline);
//					obj.put("updatetime1", TimeUtils.getCurTime());
//					RedisTools.setHashJsonObject(MainVerticle.instance.redisClient, key, obj);
//					String sql = "update userdailyonline set todayonline = ?,updatetime1=? where id = ?;";
//					JsonArray params = new JsonArray();
//					params.add(todayonline);
//					params.add(TimeUtils.getCurTime());
//					params.add(userId);
//					MysqlTool.updateToDb(MainVerticle.instance.jdbcClientGame, sql, params, res->{
//						if (res.failed()) {
//							logger.error(res.cause().getMessage());
//						}
//					});
//				}
//			} else {
//				logger.error(handler.cause().getMessage());
//			}
//		});
    }

    /***
     * 删除掉一个用户的连接
     * @param uid
     */
    public static void removeSocket(long uid, ServerWebSocket info) {
        if (info == null) {
            info = socketList.get(uid);
        }

        ServerWebSocket socket = socketList.get(uid);
        if (socket != null && socket.remoteAddress().host().equals(info.remoteAddress().host())
                && socket.remoteAddress().port() == info.remoteAddress().port()) {
			// 如果是顶号，socket会重新连接，端口会不一致，就不移除网关信息
            socketList.remove(uid);
            removeUserGateKey(uid);
            // 更新总的玩家在线数量
            if (ServerParam.USER_ONLINE_MAX > 0) {
                RedisTools.sub(RedisKey.getUserOnlineKey(), 1, h -> {
                });
            }
        }

        // 删除init锁
        RedisTools.del(RedisKey.getUserInitKey(uid));

        // 通知游戏服执行用户未入库队列
        JsonObject message = new JsonObject();
        message.put("cmd", CmdConstant.USER_OFFLINE);
        message.put("data", new JsonObject().put("userid", uid).put("socketTime", SocketInfo.getStartTime(info)));
        String ebName = EventBusConstant.getAddress(EventBusConstant.GAME_CUSTOM_LOGIC);
        EventBus eventBus = MainService.getVertx().eventBus();
        eventBus.send(ebName, message);
    }

    /***
     * 从redis中删除玩家网关服信息
     * @param uid
     */
    public static void removeUserGateKey(long uid) {
        String userGateKey = RedisKey.getUserGateKey(uid);
        RedisTools.del(userGateKey);
    }

    /***
     * 用户断线
     */
    public static void removeWebSocket(long userid, ServerWebSocket socket) {
        logger.info("removeSocket userid:" + userid + " clientIp:" + socket.remoteAddress());
        removeSocket(userid, socket);
//		ServerWebSocket info = socketList.get(userid);
//		if (info.remoteAddress().host().equals(socket.remoteAddress().host()) &&
//				info.remoteAddress().port() == socket.remoteAddress().port()){
//			logger.info("removeSocket userid:" + userid + " clientIp:" + info.remoteAddress());
//			removeSocket(userid, info);
//		}
    }
    /***
     * 用户断线
     * @param socket
     */
//	public void removeSocket(ServerWebSocket socket){
//		for(Long key:socketList.keySet()){
//			SocketInfo info = socketList.get(key);
//			ServerWebSocket v = info.getSocket();
//			//			System.out.println("socket  "+v.remoteAddress()+" uid:"+key);
//			if(socket == v){
//				logger.info("removeSocket userid:"+key+" clientIp:"+v.remoteAddress());
//				removeSocket(key,info);
//				return;
//			}
//		}
//	}

    /***
     * 检查超时的链接
     */
//	private void checkSessionTimeout() {
//		try {
//			ConcurrentHashMap<String,SessionInfo> conMap = getSessionList();
//			for (Entry<String,SessionInfo> entry : conMap.entrySet()) {
//				SessionInfo info = entry.getValue();
//				if (info.isTimeOut()) {
//					logger.info("CheckSession timeout and remove "+info.getSocket().remoteAddress());
//					conMap.remove(entry.getKey());
//					info.getSocket().close();
//
//				}
//			}
//		} catch (Exception e) {
//			logger.error(Tool.getException(e));
//		}
//	}
    public static String getCmdName(int cmdCode) {
        EnumCmdID e = CmdEnum.EnumCmdID.forNumber(cmdCode);
        if (e == null) {//未定义的协议如果返回出错，则每次新增协议都要重新编译gate，开发阶段先注释
            logger.error("handleMessage undefine cmdCode:" + cmdCode);
            //				sendResponseError(socket,cmdCode, Constant.NET_ERROR_UNDEFINE_CMD);
            return "errCmdName." + cmdCode;
        } else {
            return e.name();
//			logger.info("handleMessage cmdName:"+cmdName);
        }
    }

    /**
     * 踢掉所有玩家，提示游戏服维护中
     */
    public static void kickAllUser() {
        logger.info("### kickAllUser !!!");
        ConcurrentHashMap<Long, ServerWebSocket> list = SocketManager.getSocketList();
        for (ServerWebSocket socket : list.values()) {
            SocketManager.kickUser(socket, ErrorWord.SERVER_MAINTAIN);
        }
    }

    /**
     * 踢掉某几个游戏服的玩家，提示游戏服维护中
     */
    public static void kickServerUser(JsonArray serveridList) {
        logger.info("### kickServerUser serverid info = " + serveridList.toString());
        ConcurrentHashMap<Long, ServerWebSocket> list = SocketManager.getSocketList();
        for (ServerWebSocket socket : list.values()) {
            int sid = SocketInfo.getServerId(socket);
            if (Tool.isInList(serveridList, sid)) {
                SocketManager.kickUser(socket, ErrorWord.SERVER_MAINTAIN);
            }
        }
    }

    /**
     * 是否是消息队列执行方式
     */
//    public static boolean isMessageQueueWay() {
//        return ServerParam.GATE_MESSAGE_WAY == 1 && ServerParam.GATE_MESSAGE_POOL_SIZE > 0;
//    }

    /**
     * 获取所有消息队列的消息数量之和
     */
    public static long getTotalMessageQueueNum() {
        long num = 0;
        for (BlockingQueue<MessageInfo> messageInfoQueue : messageQueueMap.values()) {
            num += messageInfoQueue.size();
        }
        return num;
    }

    /**
     * 添加到消息队列中
     *
     * @param userid      用户ID
     * @param messageInfo 客户端请求消息
     */
//    public static void addMessageQueue(long userid, MessageInfo messageInfo) {
//        int index = (int) (userid % ServerParam.GATE_MESSAGE_POOL_SIZE);
//        BlockingQueue<MessageInfo> messageQueue = messageQueueMap.get(index);
//        if (messageQueue != null) {
//            messageQueue.add(messageInfo);
//        } else {
//            logger.error("get messageQueue error index = " + index);
//        }
//    }

    /**
     * 请求游戏服回调方法
     */
    public static void requestGameCallback(JsonObject recData, ServerWebSocket socketInfo, long userId, DataDecoded reqPacket, long startTime) {
        if (recData != null && recData.containsKey("code")) {
            int cmdCode = reqPacket.getCmdCode();
            byte[] pbdata = reqPacket.getReqData();
            int resCode = recData.getInteger("code");
            byte[] resData = recData.getBinary("data");

            if (socketInfo != null) {
                long dealTime = System.currentTimeMillis() - startTime;//响应时间

                ResponseUtils.send(socketInfo, resCode, resData, reqPacket);

                // 监测数据
                boolean timeout = dealTime > ServerParam.LOGIC_WARNING_RUN_TIME;// 是否超时
                if (timeout) {
                    logger.warn("run logic timeout userId:{} cmdCode:{} runtime:{}", userId, cmdCode, dealTime);
                }
                GateMonitorManager.addMonitorData(cmdCode == CmdEnum.EnumCmdID.CMD_GAME_SYSTEM_INIT_VALUE, pbdata.length, resData.length, dealTime, timeout);
                // 协议监控
				MonitorManager.addProtocolMonitorData(userId, cmdCode, resCode, pbdata, resData);

                //隔天重载
                long lastTime = SocketInfo.getLastTime(socketInfo);
                SocketInfo.setLastTime(socketInfo, TimeUtils.getCurTime());
                if (cmdCode != CmdEnum.EnumCmdID.CMD_GAME_SYSTEM_INIT_VALUE && cmdCode != CmdEnum.EnumCmdID.CMD_GAME_SYSTEM_RELOAD_VALUE && lastTime > 0 && !TimeUtils.isToday(lastTime)) {
                    logger.info("-------#######-------notice system.reload userId:" + userId + " lastTime:" + TimeUtils.getTimeFullStr(lastTime) + " curTime:" + TimeUtils.getTimeFullStr(TimeUtils.getCurTime()));
                    CSGameSystemReloadNoticeResponse.Builder reload_build = CSGameSystemReloadNoticeResponse.newBuilder();
                    ResponseUtils.push(socketInfo, CmdEnum.EnumCmdID.CMD_GAME_SYSTEM_RELOAD_NOTICE_VALUE, reload_build.build().toByteArray());
                }
            }
            addMessageCache(userId, cmdCode, resCode, reqPacket.getMessageId(), resData);
        }

    }

    private static void addMessageCache(long userId, int cmdCode, int resCode, int messageId, byte[] resData) {
        if (messageId > 0 && cmdCode != CmdEnum.EnumCmdID.CMD_GAME_SYSTEM_INIT_VALUE && cmdCode != CmdEnum.EnumCmdID.CMD_GAME_SYSTEM_RELOAD_VALUE) {
//            logger.info("addMessageCache userid:{},messageId:{},cmdCode:{},resCode:{},resDataLen:{}", userId, messageId, cmdCode, resCode, resData.length);
            MessageCache messageCache = new MessageCache(cmdCode, resCode, resData, messageId, TimeUtils.getCurTime());
            messageCacheMap.put(userId, messageCache);
        } else if (messageId > 0 && cmdCode == CmdEnum.EnumCmdID.CMD_GAME_SYSTEM_INIT_VALUE) {
            // CMD_GAME_SYSTEM_INIT时重置缓存信息（如果没有重置，那么登陆之后请求X协议，然后马上重新登陆又请求X协议，就会被认定为缓存）
            messageCacheMap.remove(userId);
        }
    }

    public static MessageCache getMessageCache(long userId) {
        return messageCacheMap.get(userId);
    }

    /**
     * 检测游戏进程是否启动
     */
    public static Future<Boolean> checkGameStart(EventBus eventBus, String ebName) {
        Promise<Boolean> promise = Promise.promise();
        if (ServerConfig.isDebugServer() && !ServerConfig.isLocalServer()) {
            DeliveryOptions options = new DeliveryOptions();
            options.setSendTimeout(50);//超时时间
            eventBus.request(ebName, new JsonObject(), options, ar -> {
                if (ar.succeeded()) {
//                    logger.info("checkGameStart true");
                    promise.complete(true);
                } else {
                    logger.info("checkGameStart false");
                    promise.complete(false);
                }
            });
        } else {
            promise.complete(true);
        }
        return promise.future();
    }

}
